//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <bsp.h>
#include "uart.h"

EXTERN_C bool_t CDECL BspSetupComPort(
    ComPort comPort, const ComPortSettings *pSettings)
{
    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

//    MyCPLD_SetPwrIO(RS232_PWR, 1);  // max3244 power on

    ComPortSettings defaultSettings = {
        115200,
        ComPortByteSize_Default,
        ComPortParity_Default,
        ComPortStopBits_Default
    };

    if (NULL == pSettings) pSettings = &defaultSettings;

    // Make the line parameter
    uint_t uFCon = _FEN;
    switch (pSettings->byteSize) {
        case ComPortByteSize_5:     uFCon |= _WLEN_5;   break;
        case ComPortByteSize_6:     uFCon |= _WLEN_6;   break;
        case ComPortByteSize_7:     uFCon |= _WLEN_7;   break;
        case ComPortByteSize_8:     uFCon |= _WLEN_8;   break;
        default: return FALSE;
    }

    switch (pSettings->stopBits) {
        case ComPortStopBits_One:                       break;
        case ComPortStopBits_One5:  uFCon |= _STP2;     break;
        case ComPortStopBits_Two:   uFCon |= _STP2;     break;
        default: return FALSE;
    }

    switch (pSettings->parity) {
        case ComPortParity_No:                              break;
        case ComPortParity_Odd:     uFCon |= _PEN;          break;
        case ComPortParity_Even:    uFCon |= _PEN | _EPS;   break;
        case ComPortParity_Mark:    return FALSE;
        case ComPortParity_Space:   return FALSE;
        default: return FALSE;
    }

    // Make the baudrate divisor
    uint_t uBaudDiv = 0;
    switch (pSettings->uBaudRate) {
        case 2400:      uBaudDiv = _BAUDDIV_2400;       break;
        case 4800:      uBaudDiv = _BAUDDIV_4800;       break;
        case 9600:      uBaudDiv = _BAUDDIV_9600;       break;
        case 19200:     uBaudDiv = _BAUDDIV_19200;      break;
        case 28800:     uBaudDiv = _BAUDDIV_28800;      break;
        case 38400:     uBaudDiv = _BAUDDIV_38400;      break;
        case 57600:     uBaudDiv = _BAUDDIV_57600;      break;
        case 76800:     uBaudDiv = _BAUDDIV_76800;      break;
        case 115200:    uBaudDiv = _BAUDDIV_115200;     break;
        case 153600:    uBaudDiv = _BAUDDIV_153600;     break;
        case 230400:    uBaudDiv = _BAUDDIV_230400;     break;
        case 460800:    uBaudDiv = _BAUDDIV_460800;     break;
        default: return FALSE;
    }

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    // Enable uart
    Outl(_UART_CON(iobase), Inl(_UART_CON(iobase)) | _UARTEN | _SIRD);

    // Disable uart interrupt
    Outl(_UART_INTEN(iobase), 0);

    // Set baut rate and line parameters
    Outl(_UART_BRCON(iobase), uBaudDiv);
    Outl(_UART_FCON(iobase), uFCon);

    // Clear up uart fifo
    while (!(Inl(_UART_STATUS(iobase)) & _RXFE)) {
        Inl(_UART_DATA(iobase));
    }

    return TRUE;
}

EXTERN_C bool_t CDECL BspWriteComPort(ComPort comPort, uint8_t u8Data)
{
    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    while (Inl(_UART_STATUS(iobase)) & _TXFF);

    Outb(_UART_DATA(iobase), u8Data);

    return TRUE;
}

EXTERN_C bool_t CDECL BspReadComPort(ComPort comPort, uint8_t *pu8Data)
{
    if (NULL == pu8Data) return FALSE;

    *pu8Data = 0;

    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    while (Inl(_UART_STATUS(iobase)) & _RXFE);

    uint_t uData = Inl(_UART_DATA(iobase));

    if (uData & (_FE | _PE | _OE | _BE)) return FALSE;

    *pu8Data = uData & 0xff;
    return TRUE;
}

EXTERN_C bool_t CDECL TryReadComPort(
    ComPort comPort, uint8_t *pu8Data, uint_t uTimes)
{
    if (NULL == pu8Data) return FALSE;

    *pu8Data = 0;

    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    while (Inl(_UART_STATUS(iobase)) & _RXFE) {
        if (0 == uTimes--) return FALSE;
    }

    uint_t uData = Inl(_UART_DATA(iobase));

    if (uData & (_FE | _PE | _OE | _BE)) return FALSE;

    *pu8Data = uData & 0xff;
    return TRUE;
}
